package com.sunwayworld.basemodule.business.report.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.sunwayworld.basemodule.business.batch.bean.LimsBatchSampleProductBean;
import com.sunwayworld.basemodule.business.batch.service.LimsBatchSampleProductService;
import com.sunwayworld.basemodule.business.folder.bean.LimsFolderBean;
import com.sunwayworld.basemodule.business.folder.service.LimsFolderService;
import com.sunwayworld.basemodule.business.report.bean.LimsReportBean;
import com.sunwayworld.basemodule.business.report.dao.LimsReportDao;
import com.sunwayworld.basemodule.business.report.restful.dto.AnalyteDTO;
import com.sunwayworld.basemodule.business.report.restful.dto.ReportBackDataReqDTO;
import com.sunwayworld.basemodule.business.report.restful.dto.ResultDataDTO;
import com.sunwayworld.basemodule.business.report.service.LimsReportService;
import com.sunwayworld.basemodule.business.result.bean.LimsResultBean;
import com.sunwayworld.basemodule.business.result.service.LimsResultService;
import com.sunwayworld.basemodule.business.utils.FtpsUtils;
import com.sunwayworld.basemodule.business.utils.HttpReqUtils;
import com.sunwayworld.basemodule.business.utils.MD5;
import com.sunwayworld.basemodule.business.utils.SHA256;
import com.sunwayworld.basemodule.common.utils.AsposeWordUtils;
import com.sunwayworld.cloud.lims.reporttemplate.bean.LimsReportTemplateBean;
import com.sunwayworld.cloud.lims.reporttemplate.service.LimsReportTemplateService;
import com.sunwayworld.cloud.lims.reporttemplate.support.util.LimsFileUtils;
import com.sunwayworld.cloud.lims.reporttemplate.support.word.LimsWordReportGenerator;
import com.sunwayworld.framework.at.annotation.AuditTrailEntry;
import com.sunwayworld.framework.at.annotation.AuditTrailType;
import com.sunwayworld.framework.audit.aunnotation.Audit;
import com.sunwayworld.framework.context.ApplicationContextHelper;
import com.sunwayworld.framework.context.LocalContextHelper;
import com.sunwayworld.framework.database.sql.Order;
import com.sunwayworld.framework.exception.FtpException;
import com.sunwayworld.framework.exception.InvalidDataException;
import com.sunwayworld.framework.executor.ExecutorHelper;
import com.sunwayworld.framework.i18n.I18nHelper;
import com.sunwayworld.framework.io.file.FilePathDTO;
import com.sunwayworld.framework.io.file.FilePathManager;
import com.sunwayworld.framework.io.file.FileScope;
import com.sunwayworld.framework.mybatis.mapper.MapperParameter;
import com.sunwayworld.framework.mybatis.mapper.MatchPattern;
import com.sunwayworld.framework.mybatis.mapper.SearchFilter;
import com.sunwayworld.framework.restful.data.RestJsonWrapperBean;
import com.sunwayworld.framework.security.bean.LoginUser;
import com.sunwayworld.framework.spring.annotation.GikamBean;
import com.sunwayworld.framework.support.auditable.bean.CoreBpmnParameterDTO;
import com.sunwayworld.framework.utils.*;
import com.sunwayworld.module.item.file.bean.CoreFileBean;
import com.sunwayworld.module.item.file.manager.CoreFileManager;
import com.sunwayworld.module.item.file.service.CoreFileService;
import com.sunwayworld.module.item.file.utils.CoreFileUtils;
import com.sunwayworld.module.sys.bpmn.bean.CoreBpmnInstanceStatusDTO;
import com.sunwayworld.module.sys.bpmn.engine.CoreBpmnRuntimeService;
import com.sunwayworld.module.sys.bpmn.engine.CoreBpmnRuntimeSource;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

@Repository
@GikamBean
public class LimsReportServiceImpl implements LimsReportService {

    private static final Logger log = LogManager.getLogger(LimsReportServiceImpl.class);

    @Value("${ftp.url}")
    private String ftpUrl;// ftp服务器地址

    @Value("${ftp.port}")
    private String ftpPort;// ftp端口

    @Value("${ftp.userName}")
    private String ftpUserName;// ftp用户名

    @Value("${ftp.password}")
    private String ftpPassword;// ftp密码

    @Value("${report.reportDataBackUrl}")
    private String reportDataBackUrl;// 报告结构化数据回传接口路径

    @Value("${report.appkey}")
    private String appkey;// 报告结构化数据回传接口appkey

    @Value("${report.saltKey}")
    private String saltKey;// 报告结构化数据回传接口saltKey

    @Autowired
    private LimsReportDao limsReportDao;

    @Autowired
    private LimsReportTemplateService reportTemplateService;

    @Autowired
    @Lazy
    private CoreFileService coreFileService;

    @Autowired
    @Lazy
    private LimsResultService resultService;

    @Autowired
    @Lazy
    private CoreFileManager fileManager;

    @Autowired
    @Lazy
    private LimsBatchSampleProductService limsBatchSampleProductService;

    @Autowired
    @Lazy
    private LimsFolderService limsFolderService;

    @Override
    @SuppressWarnings("unchecked")
    public LimsReportDao getDao() {
        return limsReportDao;
    }

    // 系统参数：REPORT_DATA_PRODUCTCODE，特定报告结构化数据的检测项目编码默认值
    public static final String reportProductCode = "P00026";
    // 检测结论获取指定分析项的值：值名称
    public static final String concusionAnalyte = "检测结论";

    @Override
    @Transactional
    @AuditTrailEntry(AuditTrailType.INSERT)
    public Long insert(RestJsonWrapperBean jsonWrapper) {
        LimsReportBean limsReport = jsonWrapper.parseUnique(LimsReportBean.class);
        limsReport.setId(ApplicationContextHelper.getNextIdentity());
        getDao().insert(limsReport);
        return limsReport.getId();
    }

    @Override
    @Transactional
    public void createReports(String ids) {
        List<Long> reportIds = Arrays.stream(ids.split(",")).map(NumberUtils::parseLong).collect(Collectors.toList());
        List<LimsReportBean> reportBeans = selectListByFilter(SearchFilter.instance()
                .match("id", reportIds).filter(MatchPattern.OR)
                .match("processStatus", "draft").filter(MatchPattern.SB));
        Optional<LimsReportBean> first = reportBeans.stream().filter(r -> r.getReportTemplateId() == null).findFirst();
        if (first.isPresent()) throw new InvalidDataException(I18nHelper.getMessage("NO.ADAPTER.ANY.REPORT.TEMPLATE"));
        createReport(reportBeans, false);
    }

    @Override
    @Transactional
    /*
     * 根据报告IDS批量生成报告
     * */
    public void createReport(RestJsonWrapperBean wrapper) {
        List<LimsReportBean> reports = wrapper.parse(LimsReportBean.class);
        if (CollectionUtils.isEmpty(reports)) {
            return;
        }
        List<Long> reportIds = reports.stream().map(LimsReportBean::getId).collect(Collectors.toList());
        List<LimsReportBean> reportBeans = selectListByFilter(SearchFilter.instance()
                .match("ID", reportIds).filter(MatchPattern.OR));
        if (CollectionUtils.isEmpty(reportBeans)) {
            return;
        }
        for (LimsReportBean report: reportBeans) {
            if (ObjectUtils.isEmpty(report.getReportTemplateId())) {
                // 只要有一个报告没有模版信息，则不生成
                throw new InvalidDataException(I18nHelper.getMessage("NO.ADAPTER.ANY.REPORT.TEMPLATE"));
            }
        }
        // 调用报告生成
        createReports(reportBeans);
    }

    /*
     * 生成doc及pdf两个报告文件
     * */
    private void createReports(List<LimsReportBean> reportBeans) {
        if (CollectionUtils.isEmpty(reportBeans)) return;
        // 查询所有报告的模版
        List<Long> reportTemplateIds = reportBeans.stream()
                .map(LimsReportBean::getReportTemplateId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(reportTemplateIds)) {
            return;
        }
        List<LimsReportTemplateBean> reportTemplates = reportTemplateService.selectListByIds(reportTemplateIds);
        // 未查询到模版数据
        if (CollectionUtils.isEmpty(reportTemplates)) {
            // 一个模版也没有
            throw new InvalidDataException(I18nHelper.getMessage("NO.ADAPTER.ANY.REPORT.TEMPLATE"));
        }
        if(reportTemplates.stream().anyMatch(t -> t.getTemplateFileId() == null)) {
            throw new InvalidDataException("LIMS.MODULE.REPORTTEMPLATES.TIP.NO_TEMPLATE_FILE");
        }
        List<Long> fileIds = reportTemplates.stream().map(LimsReportTemplateBean::getTemplateFileId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        List<CoreFileBean> fileBeans = coreFileService.selectListByIds(fileIds);
        for(CoreFileBean fileBean : fileBeans) {
            Path templateFilePath = LimsFileUtils.getFilePath(fileBean);
            File file = templateFilePath.toFile();
            if (!file.exists()) {
                throw new InvalidDataException("LIMS.MODULE.REPORTTEMPLATES.TIP.NO_TEMPLATE_FILE");
            }
        }

        Map<Long, LimsReportTemplateBean> reportTemplateByIdMap =
                reportTemplates.stream().collect(Collectors.toMap(LimsReportTemplateBean::getId, bean->bean));

        // 所有的pdf文件，需要同步到ftp指定目录下，由于pdf重命名，用map保存
        List<Callable<Map<String, FilePathDTO>>> taskList = new ArrayList<>();
        LoginUser loginPrincipal = LocalContextHelper.getLoginPrincipal();
        for (LimsReportBean reportBean : reportBeans) {
            LimsReportTemplateBean template = reportTemplateByIdMap.get(reportBean.getReportTemplateId());
            taskList.add(() -> createReport(reportBean, template, loginPrincipal));
        }
        // 更新报告的pdf及创建人信息
        getDao().fastUpdate(reportBeans, "reportFileName", "reportFileId", "generator", "generatorId");
        // 事务多线程创建报告
        List<Map<String, FilePathDTO>> ftpFilePathMaps = ExecutorHelper.transactionAwareSubmit(taskList);

        // ftp文件上传，覆盖更新
        ftpsUpload(ftpFilePathMaps);
        // 报告结构化数据回传
        reportDataBack(reportBeans);
    }

    private Map<String, FilePathDTO> createReport(LimsReportBean reportBean, LimsReportTemplateBean limsWordTemplateBean, LoginUser loginPrincipal) {
        LocalContextHelper.setUserLogin(loginPrincipal);
        MapperParameter parameter = new MapperParameter();
        parameter.put("reportno", reportBean.getReportNo()); // 传参数，报告编号
        LimsWordReportGenerator wordWordGenerator = new LimsWordReportGenerator(limsWordTemplateBean, parameter);
        // 文件名（不含扩展名，pdf和doc公用：送检单位简称+分院样本编号+姓名）
        String customerNickName = !StringUtils.isEmpty(reportBean.getExt$Item("CUSTOMERNICKNAME"))
                ?reportBean.getExt$Item("CUSTOMERNICKNAME"):"";
        String hissampleCodes = !StringUtils.isEmpty(reportBean.getHissampleCodes())
                ?reportBean.getHissampleCodes():"";
        String sampleName = !StringUtils.isEmpty(reportBean.getExt$Item("SAMPLENAME"))
                ?reportBean.getExt$Item("SAMPLENAME"):"";
        final String preFileName =  customerNickName + hissampleCodes + sampleName;
        // 爱康分院检线号-体检号-项目编码
        String akHospitalLine = !StringUtils.isEmpty(reportBean.getExt$Item("AKHOSPITALLINE"))
                ?reportBean.getExt$Item("AKHOSPITALLINE"):"";
        final String ftpFileName =  akHospitalLine + "-" + hissampleCodes + "-" + reportBean.getProductCode() + ".pdf";
        // 生成doc，自定义生成的报告名称
        FilePathDTO templateFile = wordWordGenerator.buildReport();
        Path localPath = FilePathManager.getLocalPath(templateFile);
        // 拷贝一份doc报告文件
        FilePathDTO docFile = FilePathDTO.of(FileScope.temp.name(),
                templateFile.getTimestamp(), templateFile.getIrregularName(),preFileName + ".docx");
        // 从模板临时附件拷贝到doc报告文件
        AsposeWordUtils.doc2docx(localPath.toString(), FilePathManager.getLocalPath(docFile).toString());
        // 生成一份pdf文件
        FilePathDTO pdfFile = FilePathDTO.of(FileScope.temp.name(),
                templateFile.getTimestamp(), templateFile.getIrregularName(),preFileName + ".pdf");
        // 将模版生成的doc文件转为pdf文件
        AsposeWordUtils.doc2pdf(localPath.toString(), FilePathManager.getLocalPath(pdfFile).toString());
        CoreFileBean pdfCoreFileBean = new CoreFileBean();
        String targetId = getDao().getTable() + "$" + reportBean.getId();
        pdfCoreFileBean.setTargetId(targetId);
        // 在线生成
        pdfCoreFileBean.setBizCategory("audit");
        Long pdfKey = coreFileService.upload(pdfCoreFileBean, FilePathManager.getLocalPath(pdfFile));
        // pdf信息保存到报告中
        reportBean.setReportFileName(preFileName + ".pdf");
        reportBean.setReportFileId(pdfKey);
        reportBean.setGenerator(LocalContextHelper.getLoginUserName());
        reportBean.setGeneratorId(LocalContextHelper.getLoginUserId());
        Map<String, FilePathDTO> filePathDTOMap = new HashMap<>();
        filePathDTOMap.put(ftpFileName, pdfFile);
        // doc报告文件
        CoreFileBean docCoreFileBean = new CoreFileBean();
        docCoreFileBean.setTargetId(targetId);
        docCoreFileBean.setBizCategory("audit");
        coreFileService.upload(docCoreFileBean, FilePathManager.getLocalPath(docFile));
        // 删除模版生成的文件
        FileUtils.deleteQuietly(localPath.toFile());
        LocalContextHelper.removeUserLogin();
        return filePathDTOMap;

    }

    /*
     * 用于实验模版完成自动生成报告文件
     * 生成doc及pdf两个报告文件
     * 出现无法生成的，不报错，继续下一个
     * */
    private void createReports2(List<LimsReportBean> reportBeans) {
        if (CollectionUtils.isEmpty(reportBeans)) return;
        // 查询所有报告的模版
        List<Long> reportTemplateIds = reportBeans.stream()
                .map(LimsReportBean::getReportTemplateId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(reportTemplateIds)) {
            return;
        }
        List<LimsReportTemplateBean> reportTemplates = reportTemplateService.selectListByIds(reportTemplateIds);
        // 未查询到模版数据
        if (CollectionUtils.isEmpty(reportTemplates)) {
            return;
        }
        Map<Long, LimsReportTemplateBean> reportTemplateByIdMap =
                reportTemplates.stream().collect(Collectors.toMap(bean -> bean.getId(),bean->bean));
        MapperParameter parameter = new MapperParameter();
        // 所有的pdf文件，需要同步到ftp指定目录下，由于pdf重命名，用map保存
        List<Map<String, FilePathDTO>> ftpFilePathMaps = new ArrayList<>();
        for (LimsReportBean reportBean : reportBeans) {
            parameter.clear();
            parameter.put("reportno", reportBean.getReportNo()); // 传参数，报告编号
            LimsReportTemplateBean limsWordTemplateBean = reportTemplateByIdMap.get(reportBean.getReportTemplateId());
            if (limsWordTemplateBean == null) {
                // 继续下一个
                continue;
            }
            LimsWordReportGenerator wordWordGenerator = new LimsWordReportGenerator(limsWordTemplateBean, parameter);
            // 文件名（不含扩展名，pdf和doc公用：送检单位简称+分院样本编号+姓名）
            String customerNickName = !StringUtils.isEmpty(reportBean.getExt$Item("CUSTOMERNICKNAME"))
                    ?reportBean.getExt$Item("CUSTOMERNICKNAME"):"";
            String hissampleCodes = !StringUtils.isEmpty(reportBean.getHissampleCodes())
                    ?reportBean.getHissampleCodes():"";
            String sampleName = !StringUtils.isEmpty(reportBean.getExt$Item("SAMPLENAME"))
                    ?reportBean.getExt$Item("SAMPLENAME"):"";
            final String preFileName =  customerNickName + hissampleCodes + sampleName;
            // 爱康分院检线号-体检号-项目编码
            String akHospitalLine = !StringUtils.isEmpty(reportBean.getExt$Item("AKHOSPITALLINE"))
                    ?reportBean.getExt$Item("AKHOSPITALLINE"):"";
            final String ftpFileName =  akHospitalLine + "-" + hissampleCodes + "-" + reportBean.getProductCode() + ".pdf";
            // 生成doc，自定义生成的报告名称
            FilePathDTO templateFile = wordWordGenerator.buildReport();
            Path localPath = FilePathManager.getLocalPath(templateFile);
            // 拷贝一份doc报告文件
            FilePathDTO docFile = FilePathDTO.of(FileScope.temp.name(),
                    templateFile.getTimestamp(), templateFile.getIrregularName(),preFileName + ".docx");
            // 从模板临时附件拷贝到doc报告文件
            AsposeWordUtils.doc2docx(localPath.toString(), FilePathManager.getLocalPath(docFile).toString());
            // 生成一份pdf文件
            FilePathDTO pdfFile = FilePathDTO.of(FileScope.temp.name(),
                    templateFile.getTimestamp(), templateFile.getIrregularName(),preFileName + ".pdf");
            // 将模版生成的doc文件转为pdf文件
            AsposeWordUtils.doc2pdf(localPath.toString(), FilePathManager.getLocalPath(pdfFile).toString());
            CoreFileBean pdfCoreFileBean = new CoreFileBean();
            String targetId = getDao().getTable() + "$" + reportBean.getId();
            pdfCoreFileBean.setTargetId(targetId);
            pdfCoreFileBean.setBizCategory("auto");
            Long pdfKey = coreFileService.upload(pdfCoreFileBean, FilePathManager.getLocalPath(pdfFile));
            reportBean.setReportFileName(preFileName + ".pdf");
            reportBean.setReportFileId(pdfKey);
            reportBean.setGenerator(LocalContextHelper.getLoginUserName());
            reportBean.setGeneratorId(LocalContextHelper.getLoginUserId());
            Map<String, FilePathDTO> filePathDTOMap = new HashMap<>();
            filePathDTOMap.put(ftpFileName, pdfFile);
            ftpFilePathMaps.add(filePathDTOMap);
            // doc报告文件
            CoreFileBean docCoreFileBean = new CoreFileBean();
            docCoreFileBean.setTargetId(targetId);
            docCoreFileBean.setBizCategory("auto");
            coreFileService.upload(docCoreFileBean, FilePathManager.getLocalPath(docFile));
            // 删除模版生成的文件
            FileUtils.deleteQuietly(localPath.toFile());
        }
        // 更新报告的pdf创建信息
        getDao().fastUpdate(reportBeans, "reportFileName", "reportFileId", "generator", "generatorId");
        // ftp文件上传，覆盖更新
        ftpsUpload(ftpFilePathMaps);
        // 报告结构化数据回传
        reportDataBack(reportBeans);
    }

    @Override
    @Transactional
    /*
     * 根据报告IDS批量生成报告
     * */
    public void autoCreateReports(RestJsonWrapperBean wrapper) {
        String idsStr = wrapper.getParamValue("ids");
        if (StringUtils.isEmpty(idsStr)) {
            return;
        }
        List<Long> reportIds = Arrays.asList(idsStr.split(",")).stream().map(Long::parseLong).collect(Collectors.toList());
        List<LimsReportBean> reportBeans = selectListByFilter(SearchFilter.instance()
                .match("ID", reportIds).filter(MatchPattern.OR));
        if (CollectionUtils.isEmpty(reportBeans)) {
            return;
        }
        List<LimsReportBean> generateReports = reportBeans.stream()
                .filter(u->!ObjectUtils.isEmpty(u.getReportTemplateId())).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(generateReports)) {
            // 调用自动报告生成，除系统错误外不提示校验信息
            createReports2(reportBeans);
        }
    }

    private void createReport(List<LimsReportBean> reportBeans, boolean approve) {
        if (CollectionUtils.isEmpty(reportBeans)) return;

        MapperParameter parameter = new MapperParameter();
        for (LimsReportBean reportBean : reportBeans) {
            parameter.clear();
            parameter.put("reportNo", reportBean.getReportNo()); // 传参数，报告编号
            parameter.put("userId", approve ? LocalContextHelper.getLoginUserId() : "1");
            LimsReportTemplateBean limsWordTemplateBean = reportTemplateService.selectById(reportBean.getReportTemplateId());
            if (limsWordTemplateBean == null) {
                throw new InvalidDataException(I18nHelper.getMessage("NO.ADAPTER.ANY.REPORT.TEMPLATE"));
            }
            LimsWordReportGenerator wordWordGenerator = new LimsWordReportGenerator(limsWordTemplateBean, parameter);
            FilePathDTO templateFile = wordWordGenerator.buildReport();

            String fileName = reportBean.getExt$Item("sampleName") + " " + reportBean.getSampleCodes() + ".pdf";
            FilePathDTO pdfFile = FilePathDTO.of(FileScope.temp.name(), templateFile.getTimestamp(), templateFile.getIrregularName()
                    , fileName + ".pdf");

            Path localPath = FilePathManager.getLocalPath(templateFile);
            AsposeWordUtils.doc2pdf(localPath.toString(), FilePathManager.getLocalPath(pdfFile).toString());
            FileUtils.deleteQuietly(localPath.toFile());

            // 保存文件
            String targetId = getDao().getTable() + "$" + reportBean.getId();
            List<CoreFileBean> coreFileBeans = coreFileService.selectFileListByTargetIds(targetId);
            coreFileBeans.forEach(f -> f.setBizCategory("history"));
            // 更新为 历史报告
            coreFileService.getDao().update(coreFileBeans, "bizCategory");
            CoreFileBean coreFileBean = new CoreFileBean();
            coreFileBean.setTargetId(targetId);
            coreFileBean.setBizCategory("audit");
            coreFileBean.setVersion((long) coreFileBeans.size());
            Long uploadKey = coreFileService.upload(coreFileBean, FilePathManager.getLocalPath(pdfFile));

            reportBean.setReportFileName(fileName);
            reportBean.setReportFileId(uploadKey);
            reportBean.setGenerator(LocalContextHelper.getLoginUserName());
            reportBean.setGeneratorId(LocalContextHelper.getLoginUserId());
        }
        getDao().fastUpdate(reportBeans, "reportFileName", "reportFileId", "generator", "generatorId");
    }

    @Override
    public void approveCallback(List<Long> idList) {
        if (idList.isEmpty()) {
            return;
        }
        List<LimsReportBean> beans = this.selectListByIds(idList);
        if (beans.isEmpty()) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        beans.forEach(b -> {
            b.setSigner(LocalContextHelper.getLoginUserName());
            b.setSignerId(LocalContextHelper.getLoginUserId());
            b.setAuditTime(now);
        });
        getDao().fastUpdate(beans, "auditTime", "signer", "signerId");
    }

    @Override
    public void previewReport(String ids, HttpServletResponse response) {
        List<Long> reportIds = Arrays.stream(ids.split(",")).map(NumberUtils::parseLong).collect(Collectors.toList());
        List<LimsReportBean> reportBeans = getDao().selectListByIds(reportIds);
        if (reportBeans.isEmpty()) {
            throw new InvalidDataException(I18nHelper.getMessage("T_LIMS_REPORT.REPORTFILE_NOT_FOUND"));
        }
        Optional<LimsReportBean> any = reportBeans.stream().filter(r -> !ObjectUtils.isEmpty(r.getReportFileId())).findAny();
        if (!any.isPresent()) {
            throw new InvalidDataException(I18nHelper.getMessage("T_LIMS_REPORT.REPORTFILE_NOT_FOUND"));
        }
        List<FilePathDTO> outFileList = new ArrayList<>();
        List<Long> fileIdList = reportBeans.stream().filter(r -> !ObjectUtils.isEmpty(r.getReportFileId())).map(LimsReportBean::getReportFileId).collect(Collectors.toList());
        //List<CoreFileBean> fileBeans = coreFileService.selectFileListByTargetIds(reportIds.stream().map(id -> getDao().getTable() + "$" + id).toArray(String[]::new));
        List<CoreFileBean> fileBeans = coreFileService.selectListByIds(fileIdList);
        List<CoreFileBean> pdfFile = fileBeans.stream().filter(fileBean -> fileBean.getFileExt().equalsIgnoreCase("pdf")).collect(Collectors.toList());
        Optional<CoreFileBean> optionalCoreFileBean = fileBeans.stream().filter(f -> ArrayUtils.asList("doc", "docx").contains(f.getFileExt().toLowerCase())).findFirst();
        if (optionalCoreFileBean.isPresent()) {
            List<CoreFileBean> coreFileBeanList = fileBeans.stream().filter(f -> ArrayUtils.asList("doc", "docx").contains(f.getFileExt().toLowerCase())).collect(Collectors.toList());
            coreFileBeanList.parallelStream().forEach(p -> {
                FilePathDTO tempPdfFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), ".pdf");
                FilePathDTO tempWordFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), ".docx");
                Path copyDirPath = FilePathManager.getLocalDirPath(tempWordFile);
                Path absolutePath = FilePathManager.getLocalPath(tempWordFile);
                try {
                    Files.createDirectories(copyDirPath);
                    FilePathDTO filePathDTO = CoreFileUtils.toFilePath(p);
                    Path path = LimsFileUtils.getFilePath(p);
                    File file = path.toFile();
                    if (file.exists()) {
                        Files.copy(FilePathManager.getLocalPath(filePathDTO), FilePathManager.getLocalPath(tempWordFile));
                        AsposeWordUtils.doc2pdf(absolutePath.toString(), FilePathManager.getLocalPath(tempPdfFile).toString());
                        outFileList.add(tempPdfFile);
                    }
                } catch (IOException e) {
                    throw new InvalidDataException("create directory failed！");
                }
            });
        }
        FilePathDTO outFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(),
                DateTimeUtils.formatLocalDate(LocalDate.now()) + "-" + "报告预览.pdf");
        if (!pdfFile.isEmpty()) {
            pdfFile.forEach(r -> outFileList.add(CoreFileUtils.toFilePath(r)));
        }
        if (outFileList.isEmpty()) {
            throw new InvalidDataException("not found report file!");
        }
        coreFileService.mergePdfList(outFileList, outFile);
        FileInputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(FilePathManager.getLocalPath(outFile).toFile());
            response.setContentType("application/pdf");
            out = response.getOutputStream();
            byte[] b = new byte[4 * 1024];
            while ((in.read(b)) != -1) {
                out.write(b);
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public String downloadReports(String ids, HttpServletResponse response) {
        List<Long> reportIds = Arrays.stream(ids.split(",")).map(NumberUtils::parseLong).collect(Collectors.toList());
        List<LimsReportBean> reportBeans = selectListByIds(reportIds);
        if (CollectionUtils.isEmpty(reportBeans)) {
            throw new InvalidDataException("request parameter error!");
        }
        //过滤未上传附件报告
        // 解码报告文件是自己上传的，不校验系统生成了
/*        Optional<LimsReportBean> first = reportBeans.stream().filter(r -> ObjectUtils.isEmpty(r.getReportFileName())).findFirst();
        if (first.isPresent()) {
            StringBuilder sb = new StringBuilder();
            sb.append("the following reports：").append("</br>");
            List<LimsReportBean> beans = reportBeans.stream().filter(r -> ObjectUtils.isEmpty(r.getReportFileName())).collect(Collectors.toList());
            beans.forEach(b -> {
                sb.append(b.getReportNo()).append("<br/>");
            });
            sb.append("not found report and download failed !");
            throw new InvalidDataException(sb.toString());
        }*/
        //当前报告附件
//        List<Long> fileIdList = reportBeans.stream().map(LimsReportBean::getReportFileId).collect(Collectors.toList());
        List<CoreFileBean> fileBeans = coreFileService.selectFileListByTargetIds(reportIds.stream().map(s -> getDao().getTable() + "$" + s).toArray(String[]::new));
        if (fileBeans.isEmpty()) {
            throw new InvalidDataException("Not Found Report!");
        }
//        Optional<CoreFileBean> optionalCoreFileBean = fileBeans.stream().filter(f -> fileIdList.contains(f.getId())).findFirst();
//        if (!optionalCoreFileBean.isPresent()) {
//            throw new InvalidDataException("Not Found Report!");
//        }
//        List<CoreFileBean> targetFileList = fileBeans.stream().filter(g -> fileIdList.contains(g.getId())).collect(Collectors.toList());
        // 解码只有报告附件了
        List<CoreFileBean> atts = coreFileService.selectFileListByTargetIds(reportIds.stream().map(s -> "T_LIMS_REPORT$" + s).toArray(String[]::new)); // 报告附件
        FilePathDTO filePathDTO = FilePathDTO.of(FileScope.temp.name(), LocalDateTime.now(), StringUtils.randomUUID(16), DateTimeUtils.formatLocalDate(LocalDate.now()) + "-" + I18nHelper.getMessage("GIKAM.REPORT.PACK.DOWNLOAD_FILE_NAME") + ".zip");
        Path localDirPath = FilePathManager.getLocalDirPath(filePathDTO);
        Set<Path> zipPathSet = new HashSet<>();
        try {
            Files.createDirectories(localDirPath);
            for (LimsReportBean reportBean : reportBeans) {
//                CoreFileBean reportFile = targetFileList.stream().filter(f -> f.getTargetId().contains(reportBean.getId().toString())).findFirst().get(); // 报告文件
                List<CoreFileBean> attFiles = atts.stream().filter(f -> f.getTargetId().contains(reportBean.getId().toString())).collect(Collectors.toList());  // 报告附件
//                Path path = LimsFileUtils.getFilePath(reportFile);
//                File file = path.toFile();
//                if (file.exists()) {
//                    zipPathSet.add(path);
//                }
                if (!CollectionUtils.isEmpty(attFiles)) {
                    for (CoreFileBean attFile : attFiles) {
                        File attf = LimsFileUtils.getFilePath(attFile).toFile();
                        if (attf.exists()) {
                            zipPathSet.add(attf.toPath());
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (!zipPathSet.isEmpty()) {
            ArchiveUtils.zip(FilePathManager.getLocalPath(filePathDTO), zipPathSet.toArray(new Path[zipPathSet.size() - 1]));
            fileManager.upload(filePathDTO, FilePathManager.getLocalPath(filePathDTO));
//            zipPathSet.forEach(FileUtils::deleteRecursively);
            return FilePathManager.getUrl(filePathDTO);
        } else {
            return "-1";
        }
    }

    @Override
    public JSONObject openAllAttachs(String ids) {
        JSONObject req = new JSONObject();
        if (StringUtils.isBlank(ids)) {
            return req;
        }
        String[] targetIds = Arrays.stream(ids.split(",")).filter(s -> !StringUtils.isBlank(s)).distinct().map("T_LIMS_REPORT$"::concat).toArray(String[]::new);
        if (targetIds.length <= 0) {
            return req;
        }
        List<CoreFileBean> attachs = coreFileService.selectFileListByTargetIds(targetIds);
        List<Long> fileIds = attachs.stream().map(CoreFileBean::getId).collect(Collectors.toList());
        req.put("fileIds", fileIds);
        return req;
    }

    @Override
    public JSONObject downloadAllAttachs(String ids) {
        JSONObject req = new JSONObject();
        if (StringUtils.isBlank(ids)) {
            return req;
        }
/*        List<Long> idList = Arrays.stream(ids.split(",")).filter(s -> !StringUtils.isBlank(s)).distinct().map(Long::parseLong).collect(Collectors.toList());
        String[] targetIds = Arrays.stream(ids.split(",")).filter(s -> !StringUtils.isBlank(s)).distinct().map("T_LIMS_REPORT$"::concat).toArray(String[]::new);
        if (targetIds.length <= 0) {
            return req;
        }
        List<CoreFileBean> attachs = coreFileService.selectFileListByTargetIds(targetIds);
        List<LimsReportBean> reportBeans = this.selectListByIds(idList);
        Set<Path> zipPathSetTotal = new HashSet<>();
        FilePathDTO totalFilePathDTO = FilePathDTO.of(FileScope.temp.name(), LocalDateTime.now(), StringUtils.randomUUID(16), DateTimeUtils.formatLocalDate(LocalDate.now()) + "-" + I18nHelper.getMessage("GIKAM.REPORT.PACK.DOWNLOAD_FILE_NAME") + ".zip");
        Path totalDirPath = FilePathManager.getLocalDirPath(totalFilePathDTO);

        reportBeans.forEach(r -> {
            FilePathDTO filePathDTO = FilePathDTO.of(FileScope.temp.name(), LocalDateTime.now(), StringUtils.randomUUID(16), DateTimeUtils.formatLocalDate(LocalDate.now()) + "-" + r.getReportNo() + ".zip");
            Path localDirPath = FilePathManager.getLocalDirPath(filePathDTO);
            Set<Path> zipPathSet = new HashSet<>();
            try {
                Files.createDirectories(localDirPath);
                for (LimsReportBean reportBean : reportBeans) {
                    CoreFileBean reportFile = targetFileList.stream().filter(f -> f.getTargetId().contains(reportBean.getId().toString())).findFirst().get(); // 报告文件
                    List<CoreFileBean> attFiles = atts.stream().filter(f -> f.getTargetId().contains(reportBean.getId().toString())).collect(Collectors.toList());  // 报告附件
                    Path path = LimsFileUtils.getFilePath(reportFile);
                    File file = path.toFile();
                    if (file.exists()) {
                        zipPathSet.add(path);
                    }
                    if (!CollectionUtils.isEmpty(attFiles)) {
                        for (CoreFileBean attFile : attFiles) {
                            File attf = LimsFileUtils.getFilePath(attFile).toFile();
                            if (attf.exists()) {
                                zipPathSet.add(attf.toPath());
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        });*/

        return req;
    }

    @Override
    public void previewOnlineReports2(String ids, HttpServletResponse response) {
        List<Long> reportIds = Arrays.stream(ids.split(",")).map(NumberUtils::parseLong).collect(Collectors.toList());
        List<LimsReportBean> reportBeans = getDao().selectListByIds(reportIds);
        if (reportBeans.isEmpty()) {
            throw new InvalidDataException(I18nHelper.getMessage("T_LIMS_REPORT.REPORTFILE_NOT_FOUND"));
        }
//        Optional<LimsReportBean> any = reportBeans.stream().filter(r -> !ObjectUtils.isEmpty(r.getReportFileId())).findAny();
//        if (!any.isPresent()) {
//            throw new InvalidDataException(I18nHelper.getMessage("T_LIMS_REPORT.REPORTFILE_NOT_FOUND"));
//        }
        List<FilePathDTO> outFileList = new ArrayList<>();
//        List<Long> fileIdList = reportBeans.stream().filter(r -> !ObjectUtils.isEmpty(r.getReportFileId())).map(LimsReportBean::getReportFileId).collect(Collectors.toList());
        //List<CoreFileBean> fileBeans = coreFileService.selectFileListByTargetIds(reportIds.stream().map(id -> getDao().getTable() + "$" + id).toArray(String[]::new));

        // 查询所有附件
        String[] targetIds = Arrays.stream(ids.split(",")).filter(s -> !StringUtils.isBlank(s)).distinct().map("T_LIMS_REPORT$"::concat).toArray(String[]::new);
        if (targetIds.length <= 0) {
            return;
        }
        List<CoreFileBean> fileBeans = coreFileService.selectFileListByTargetIds(targetIds);

//        List<CoreFileBean> pdfFile = fileBeans.stream().filter(fileBean -> fileBean).collect(Collectors.toList());
        List<String> docs = Arrays.asList("doc", "docx");
        reportBeans.forEach(r -> {
            List<CoreFileBean> reportFiles = fileBeans.stream().filter(f -> f.getTargetId().contains(r.getId().toString())).sorted(Comparator.comparingLong(CoreFileBean::getId)).collect(Collectors.toList());
            if (!reportFiles.isEmpty()) {
                reportFiles.forEach(f -> {
                    if (docs.contains(f.getFileExt().toLowerCase())) {
                        FilePathDTO tempPdfFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), ".pdf");
                        FilePathDTO tempWordFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), ".docx");
                        Path copyDirPath = FilePathManager.getLocalDirPath(tempWordFile);
                        Path absolutePath = FilePathManager.getLocalPath(tempWordFile);
                        try {
                            Files.createDirectories(copyDirPath);
                            FilePathDTO filePathDTO = CoreFileUtils.toFilePath(f);
                            Path path = LimsFileUtils.getFilePath(f);
                            File file = path.toFile();
                            if (file.exists()) {
                                Files.copy(FilePathManager.getLocalPath(filePathDTO), FilePathManager.getLocalPath(tempWordFile));
                                AsposeWordUtils.doc2pdf(absolutePath.toString(), FilePathManager.getLocalPath(tempPdfFile).toString());
                                outFileList.add(tempPdfFile);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            throw new InvalidDataException("报告文件预览失败，请联系管理人员！");
                        }
                    } else if (f.getFileExt().equalsIgnoreCase("pdf")) {
                        outFileList.add(CoreFileUtils.toFilePath(f));
                    } else {

                    }
                });
            }
        });

/*        Optional<CoreFileBean> optionalCoreFileBean = fileBeans.stream().filter(f -> ArrayUtils.asList("doc", "docx").contains(f.getFileExt().toLowerCase())).findFirst();
        if (optionalCoreFileBean.isPresent()) {
            List<CoreFileBean> coreFileBeanList = fileBeans.stream().filter(f -> ArrayUtils.asList("doc", "docx").contains(f.getFileExt().toLowerCase())).collect(Collectors.toList());
            coreFileBeanList.parallelStream().forEach(p -> {
                FilePathDTO tempPdfFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), ".pdf");
                FilePathDTO tempWordFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), ".docx");
                Path copyDirPath = FilePathManager.getLocalDirPath(tempWordFile);
                Path absolutePath = FilePathManager.getLocalPath(tempWordFile);
                try {
                    Files.createDirectories(copyDirPath);
                    FilePathDTO filePathDTO = CoreFileUtils.toFilePath(p);
                    Path path = LimsFileUtils.getFilePath(p);
                    File file = path.toFile();
                    if (file.exists()) {
                        Files.copy(FilePathManager.getLocalPath(filePathDTO), FilePathManager.getLocalPath(tempWordFile));
                        AsposeWordUtils.doc2pdf(absolutePath.toString(), FilePathManager.getLocalPath(tempPdfFile).toString());
                        outFileList.add(tempPdfFile);
                    }
                } catch (IOException e) {
                    throw new InvalidDataException("create directory failed！");
                }
            });
        }
        optionalCoreFileBean = fileBeans.stream().filter(f -> ArrayUtils.asList("xls", "xlsx").contains(f.getFileExt().toLowerCase())).findFirst();
        if (optionalCoreFileBean.isPresent()) {
            List<CoreFileBean> coreFileBeanList = fileBeans.stream().filter(f -> ArrayUtils.asList("xls", "xlsx").contains(f.getFileExt().toLowerCase())).collect(Collectors.toList());
            coreFileBeanList.parallelStream().forEach(p -> {
                FilePathDTO tempPdfFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), ".pdf");
                FilePathDTO tempWordFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(), "." + p.getFileExt());
                Path copyDirPath = FilePathManager.getLocalDirPath(tempWordFile);
                Path absolutePath = FilePathManager.getLocalPath(tempWordFile);
                try {
                    Files.createDirectories(copyDirPath);
                    FilePathDTO filePathDTO = CoreFileUtils.toFilePath(p);
                    Path path = LimsFileUtils.getFilePath(p);
                    File file = path.toFile();
                    if (file.exists()) {
                        Files.copy(FilePathManager.getLocalPath(filePathDTO), FilePathManager.getLocalPath(tempWordFile));

                        SpirePdfUtils.excelToPdf(FilePathManager.getLocalPath(tempWordFile), FilePathManager.getLocalPath(tempPdfFile));
//                        Workbook wb = new Workbook();
//                        wb.loadFromFile(absolutePath.toString());
//                        wb.saveToFile(FilePathManager.getLocalPath(tempPdfFile).toString(), FileFormat.PDF);

                        outFileList.add(tempPdfFile);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new InvalidDataException("create directory failed！");
                }
            });
        }*/

        FilePathDTO outFile = FilePathDTO.of(FileScope.temp.name(), "" + System.currentTimeMillis(), UUID.randomUUID().toString(),
                DateTimeUtils.formatLocalDate(LocalDate.now()) + "-" + "报告预览.pdf");
//        if (!pdfFile.isEmpty()) {
//            pdfFile.forEach(r -> outFileList.add(CoreFileUtils.toFilePath(r)));
//        }
        if (outFileList.isEmpty()) {
            throw new InvalidDataException("not found report file!");
        }
        coreFileService.mergePdfList(outFileList, outFile);
        FileInputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(FilePathManager.getLocalPath(outFile).toFile());
            response.setContentType("application/pdf");
            out = response.getOutputStream();
            byte[] b = new byte[4 * 1024];
            while ((in.read(b)) != -1) {
                out.write(b);
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Transactional
    @AuditTrailEntry(AuditTrailType.PROCESS_COMPLETE)
    @Audit("AUDIT.PROCESS_COMPLETE")
    public List<CoreBpmnInstanceStatusDTO<Long>> completeTask(RestJsonWrapperBean wrapper) {
        List<LimsReportBean> itemList = wrapper.parse(LimsReportBean.class);
        if (itemList.isEmpty()) {
            return CollectionUtils.emptyList();
        } else {
            this.checkAndUpdateVersion(itemList);
            CoreBpmnRuntimeService runtimeService = ApplicationContextHelper.getBean(CoreBpmnRuntimeService.class);
            List<Long> itemIdList = itemList.stream().map(LimsReportBean::getId).collect(Collectors.toList());
            List<LimsReportBean> selectItemList = this.getBpmnVarsItemList(itemIdList);

            List<Long> folderIds = selectItemList.stream().map(LimsReportBean::getFolderIds).map(Long::parseLong).collect(Collectors.toList());
            List<LimsFolderBean> folderBeans = limsFolderService.selectListByIds(folderIds);
            List<LimsBatchSampleProductBean> updateBy = selectItemList.stream().map(r -> {
                LimsFolderBean folderBean = folderBeans.stream().filter(f -> f.getId().toString().equals(r.getFolderIds())).findAny().get();
                LimsBatchSampleProductBean productBean = new LimsBatchSampleProductBean();
                productBean.setProductCode(r.getProductCode());
                productBean.setBatchSampleId(folderBean.getBatchSampleId());
                productBean.setStatus("reportDone"); // 报告完成
                productBean.setReportTime(LocalDateTime.now());
                return productBean;
            }).collect(Collectors.toList());
            limsBatchSampleProductService.getDao().update(updateBy, Arrays.asList("status", "reportTime"), "batchSampleId", "productCode");

            List<CoreBpmnRuntimeSource<LimsReportBean, Long>> runtimeSourceList = this.parseAuditableRuntimeSource(itemIdList, CoreBpmnParameterDTO.of(wrapper), this.getBpmnVars(wrapper, selectItemList));
            List<CoreBpmnInstanceStatusDTO<Long>> instanceStatusList = runtimeService.completeTask(runtimeSourceList);
            this.autoPass(runtimeSourceList, instanceStatusList);
            return instanceStatusList;
        }
    }

    @Transactional
    @AuditTrailEntry(AuditTrailType.PROCESS_UNDO)
    @Audit("AUDIT.PROCESS_UNDO")
    public List<CoreBpmnInstanceStatusDTO<Long>> undo(RestJsonWrapperBean wrapper) {
        List<LimsReportBean> itemList = wrapper.parse(LimsReportBean.class);
        if (itemList.isEmpty()) {
            return CollectionUtils.emptyList();
        } else {
            this.checkAndUpdateVersion(itemList);
            CoreBpmnRuntimeService runtimeService = ApplicationContextHelper.getBean(CoreBpmnRuntimeService.class);
            List<Long> itemIdList = itemList.stream().map(LimsReportBean::getId).collect(Collectors.toList());
            List<LimsReportBean> selectItemList = this.getBpmnVarsItemList(itemIdList);

            List<Long> folderIds = selectItemList.stream().map(LimsReportBean::getFolderIds).map(Long::parseLong).collect(Collectors.toList());
            List<LimsFolderBean> folderBeans = limsFolderService.selectListByIds(folderIds);
            List<LimsBatchSampleProductBean> updateBy = selectItemList.stream().map(r -> {
                LimsFolderBean folderBean = folderBeans.stream().filter(f -> f.getId().toString().equals(r.getFolderIds())).findAny().get();
                LimsBatchSampleProductBean productBean = new LimsBatchSampleProductBean();
                productBean.setProductCode(r.getProductCode());
                productBean.setBatchSampleId(folderBean.getBatchSampleId());
                productBean.setStatus("reportUndo"); // 报告完成
                productBean.setReportTime(null);
                return productBean;
            }).collect(Collectors.toList());
            limsBatchSampleProductService.getDao().update(updateBy, Arrays.asList("status", "reportTime"), "batchSampleId", "productCode");

            List<CoreBpmnRuntimeSource<LimsReportBean, Long>> runtimeSourceList = this.parseUndoableRuntimeSource(itemIdList, CoreBpmnParameterDTO.of(wrapper), this.getBpmnVars(wrapper, selectItemList));
            return runtimeService.undo(runtimeSourceList);
        }
    }

    @Override
    public JSONObject uploadTjReport(RestJsonWrapperBean wrapper) {
        JSONObject req = new JSONObject();
        req.put("flag", true);

        List<Long> ids = wrapper.parseId(Long.class);
        List<CoreFileBean> reportFileList = coreFileService.selectListByIds(ids);
        StringBuilder sb = new StringBuilder();
        Set<String> sampleCodes = new HashSet<>();
        //校验命名格式
        reportFileList.forEach((coreFileBean) -> {
            String name = coreFileBean.getName();
            String[] split = name.split("-");
            if (split.length < 2) {
                sb.append(name).append("<br/>");
            } else {
                sampleCodes.add(split[0]);
            }
        });
        if (sb.length() > 0) {
            req.put("flag", false);
            req.put("msg", sb.append("文件名称不符合，请检查！"));
            return req;
        }
        List<CoreFileBean> distinctFiles = new ArrayList<>();
        reportFileList.stream().collect(Collectors.groupingBy(CoreFileBean::getName)).forEach((name, temp) -> {
            distinctFiles.add(temp.get(0));
        });
        List<LimsReportBean> reportBeans = this.selectListByFilter(SearchFilter.instance().match("sampleCodes", new ArrayList<>(sampleCodes)).filter(MatchPattern.OR), Order.desc("id"));
        distinctFiles.forEach(df -> {
            String[] split = df.getName().split("-");
            String sampleCode = split[0];
            String productName = split[1];
            if (split.length == 2) {
                productName = productName.substring(0, productName.indexOf("."));
            }
            String finalProductName = productName;
            reportBeans.stream().filter(r -> StringUtils.equals(r.getSampleCodes(), sampleCode) && StringUtils.equals(r.getProductName(), finalProductName)).findFirst().ifPresent(r -> {
                df.setTargetId(getDao().getTable().concat("$").concat(r.getId().toString()));
            });
        });
        coreFileService.getDao().fastUpdate(distinctFiles, "targetId");

        return req;
    }

    private static String getEncodedName(FTPClient ftpClient, String name) {
        try {
            return new String(name.toString().getBytes(ftpClient.getControlEncoding()), "ISO-8859-1");
        } catch (IOException var3) {
            throw new FtpException(var3);
        }
    }

    /*
     * ftp文件上传
     * */
    private void ftpUpload(List<Map<String, FilePathDTO>> ftpFilePathMaps){
        FTPClient ftpClient = null;
        InputStream is = null;
        if (StringUtils.isEmpty(ftpUrl) || StringUtils.isEmpty(ftpPort)
                || StringUtils.isEmpty(ftpUserName) || StringUtils.isEmpty(ftpPassword)) {
            log.error("ftp配置信息异常");
            return;
        }
        try {
            // ftp文件上传
            if (!CollectionUtils.isEmpty(ftpFilePathMaps)) {
                // 获取ftp连接
                ftpClient = FtpUtils.
                        getFtpClient(ftpUrl, Integer.valueOf(ftpPort), ftpUserName, ftpPassword);
                if (ftpClient == null) {
                    log.error("ftp连接异常");
                    return;
                }
                for (Map<String, FilePathDTO> ftpFilePathMap: ftpFilePathMaps) {
                    Set<String> keySet = ftpFilePathMap.keySet();
                    String fileName = "";
                    for (String key : keySet) {
                        fileName = key;
                    }
                    FilePathDTO filePathDTO = ftpFilePathMap.get(fileName);
                    is = Files.newInputStream(FilePathManager.getLocalPath(filePathDTO));
                    ftpClient.storeFile(getEncodedName(ftpClient, fileName), is);
                    log.info("文件上传ftp成功：" + fileName);
                }
            }
        } catch (Exception e) {
            log.error("ftp异常", e);
            e.printStackTrace();
        } finally {
            if (ftpClient != null) {
                try {
                    ftpClient.disconnect();
                } catch (Exception e) {
                    log.error("ftp连接关闭异常", e);
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (Exception e) {
                    log.error("ftp文件输入流关闭异常", e);
                    e.printStackTrace();
                }
            }
        }
    }

    /*
     * 加密ftp文件上传
     * */
    private void ftpsUpload(List<Map<String, FilePathDTO>> ftpFilePathMaps){
        FTPSClient ftpsClient = null;
        InputStream is = null;
        if (StringUtils.isEmpty(ftpUrl) || StringUtils.isEmpty(ftpPort)
                || StringUtils.isEmpty(ftpUserName) || StringUtils.isEmpty(ftpPassword)) {
            log.error("ftp配置信息异常");
            return;
        }
        try {
            // ftp文件上传
            if (!CollectionUtils.isEmpty(ftpFilePathMaps)) {
                // 获取ftp连接
                ftpsClient = FtpsUtils.getFtpsClient(ftpUrl, Integer.valueOf(ftpPort), ftpUserName, ftpPassword);
                if (ftpsClient == null) {
                    log.error("ftp连接异常");
                    return;
                }
                for (Map<String, FilePathDTO> ftpFilePathMap: ftpFilePathMaps) {
                    Set<String> keySet = ftpFilePathMap.keySet();
                    String fileName = "";
                    for (String key : keySet) {
                        fileName = key;
                    }
                    FilePathDTO filePathDTO = ftpFilePathMap.get(fileName);
                    is = Files.newInputStream(FilePathManager.getLocalPath(filePathDTO));
                    String dir = "/";
                    ftpsClient.changeWorkingDirectory(dir);
                    boolean saveFlag = ftpsClient.storeFile(getEncodedName(ftpsClient, fileName), is);
                    if (saveFlag) {
                        log.info("文件上传ftp成功：" + fileName);
                    } else {
                        log.info("文件上传ftp失败：" + fileName);
                    }
                }
            }
        } catch (Exception e) {
            log.error("ftp异常", e);
            e.printStackTrace();
        } finally {
            if (ftpsClient != null) {
                try {
                    ftpsClient.logout();
                    ftpsClient.disconnect();
                } catch (Exception e) {
                    log.error("ftp连接关闭异常", e);
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (Exception e) {
                    log.error("ftp文件输入流关闭异常", e);
                    e.printStackTrace();
                }
            }
        }
    }

    private void reportDataBack(List<LimsReportBean> reports){
        if (CollectionUtils.isEmpty(reports)) {
            return;
        }
        if (StringUtils.isEmpty(reportDataBackUrl) || StringUtils.isEmpty(appkey) || StringUtils.isEmpty(saltKey)) {
            log.error("报告结构化数据回传接口配置异常");
            return;
        }
        try {
            Gson gson =  new Gson();
            String url = reportDataBackUrl.replace("{appkey}", appkey);
            // 报告结构化数据回传
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            List<LimsReportBean> reportsFolder = reports
                .stream().filter(u->!StringUtils.isEmpty(u.getFolderIds())).collect(Collectors.toList());
            // 收集folderId
            List<Long> folderIds = reportsFolder.stream().map(u->Long.valueOf(u.getFolderIds())).distinct().collect(Collectors.toList());
            List<LimsResultBean> results = resultService.selectListByFilter(SearchFilter
                .instance().match("folderId", folderIds).filter(MatchPattern.OR));
            Map<Long, List<LimsResultBean>> resultByFolderMap = null;
            if (!CollectionUtils.isEmpty(results)) {
                resultByFolderMap = results.stream().collect(Collectors.groupingBy(u->u.getFolderId()));
            }
            // 获取系统参数，默认值为：P00026
            String reportProductCodes = ApplicationContextHelper.getConstantValue("REPORT_DATA_PRODUCTCODE", reportProductCode);
            for (LimsReportBean report : reports) {
                // 回传报告文件数据
                ReportBackDataReqDTO reqDTO = new ReportBackDataReqDTO();
                reqDTO.setMd5(UUID.randomUUID().toString());
                reqDTO.setUuid(UUID.randomUUID().toString());
                // 样本的分院检线号：akHospitalLine
                reqDTO.setHospId(report.getExt$Item("AKHOSPITALLINE"));
                reqDTO.setWorkNo(report.getHissampleCodes());
                reqDTO.setProjectType(report.getProductCode());
                ResultDataDTO resultDataDTO = new ResultDataDTO();
                // 分析项的值，用list保存
                List<AnalyteDTO> analyteDTOS = new ArrayList<>();
                if (resultByFolderMap != null && !StringUtils.isEmpty(report.getFolderIds())) {
                    // 获取当前报告下的分析项
                    List<LimsResultBean> resultByFolder = resultByFolderMap.get(Long.valueOf(report.getFolderIds()));
                    if (!CollectionUtils.isEmpty(resultByFolder)) {
                        for (LimsResultBean result: resultByFolder) {
                            // 每一项分析项的内容
                            AnalyteDTO analyteDTO = new AnalyteDTO();
                            analyteDTO.setDetectionIndicator(result.getAnalyte());
                            analyteDTO.setRelativeCount(result.getFinalResult());
                            analyteDTO.setReferenceRange(result.getReferenceRange());
                            // 分析项编码
                            analyteDTO.setIndicatorCode(result.getAnalyteCode());
                            analyteDTOS.add(analyteDTO);
                        }
                    }
                }
                // 检测结论：系统参数：REPORT_DATA_PRODUCTCODE下配置的检测项目，该值取分析项名为："检测结论"的结果值
                if (reportProductCodes.contains(report.getProductCode()) && !CollectionUtils.isEmpty(analyteDTOS)) {
                    // 是否有名称等为：检测结论的分析项
                    Optional<AnalyteDTO> conclusionOp = analyteDTOS.stream().filter(u->
                        StringUtils.equals(u.getDetectionIndicator(), concusionAnalyte)).findFirst();
                    if (conclusionOp.isPresent()) {
                        resultDataDTO.setDiagnosisConclusion(conclusionOp.get().getRelativeCount());
                        // 在分析项列表中移除：检测结论分析项
                        analyteDTOS.remove(conclusionOp.get());
                    } else {
                        // 没有，则为""
                        resultDataDTO.setDiagnosisConclusion("");
                    }
                }
                // 不管是否有分析项，都有这个节点，没有，则为空的[]
                resultDataDTO.setiKangTbnkReportResults(analyteDTOS);
                // 检测方法名称
                resultDataDTO.setExperimentalMethod(report.getExt$Item("TEST"));
                // 暂时给定值：北京艾纳医学检验实验室
                resultDataDTO.setMainLaboratory("北京艾纳医学检验实验室");
                resultDataDTO.setMainOperator(report.getExt$Item("MAINOPERATOR"));
                resultDataDTO.setMainReviewer(report.getExt$Item("MAINREVIEWER"));
                resultDataDTO.setReceiveTime(report.getExt$Item("COLLECTTIME"));
                resultDataDTO.setReportTime(LocalDateTime.now().format(dateTimeFormatter));
                resultDataDTO.setSampleStatus("正常");
                resultDataDTO.setSampleType(report.getExt$Item("SAMPLETYPE"));
                reqDTO.setData(resultDataDTO);
                // 请求数据
                String requestJson = gson.toJson(reqDTO);
                // 签名
                String s1 = MD5.getMD5(requestJson.getBytes()).toUpperCase();
                String s2 = new StringBuilder("appkey").append("=").append(appkey)
                    .append("&").append("content").append("=").append(s1)
                    .append("&").append("salt_key").append("=").append(saltKey).toString();
                String sign = SHA256.getSHA256(s2, saltKey).toUpperCase();
                url = url.replace("{sign}", sign);
                log.info("报告结构化数据接口请求地址：" + url);
                String response = HttpReqUtils.sendPost(url, requestJson);
            }
        } catch (Exception e) {
            log.error("结构化数据回传异常", e);
            e.printStackTrace();
        }
    }

    /**
     * Description: 向FTP服务器上传文件
     * @param FTP_HOST FTP服务器hostname
     * @param PROT FTP服务器端口
     * @param FTP_USERNAME FTP登录账号
     * @param FTP_PASSWORD FTP登录密码
     * @param FTP_PATH FTP服务器保存目录
     * @param filename 上传到FTP服务器上的文件名（可自行定义）
     * @param input 输入流
     * @return 成功返回true，否则返回false
     */
    public static boolean uploadFile(String FTP_HOST,int PROT,String FTP_USERNAME, String FTP_PASSWORD, String FTP_PATH, String filename, InputStream input) {
        FTPSClient ftp = new FTPSClient("SSL");
        boolean bo = false;
        try {
            ftp.connect(FTP_HOST, PROT);//连接FTP服务器
            ftp.login(FTP_USERNAME, FTP_PASSWORD);//登录
            ftp.enterLocalPassiveMode();
            ftp.setFileType(FTPSClient.BINARY_FILE_TYPE);
            ftp.execPBSZ(0);
            ftp.execPROT("P");
            ftp.changeWorkingDirectory(FTP_PATH);
            ftp.storeFile(new String(filename.getBytes("UTF-8"),"iso-8859-1"),input);
            ftp.logout();
            bo = true;
        } catch (IOException e){
            log.error(e.toString(),e);
        } finally {
            if (ftp.isConnected()) {
                try {
                    ftp.disconnect();
                } catch (IOException ioe) {
                    log.error("ftp异常",ioe);
                }
            }
            try {
                input.close();
            } catch (IOException e) {
                log.error("input关闭异常",e);
            }
        }
        return bo;
    }

    public static void main(String[] args) {
        try {
            File file = new File("C:/share/1H663482190苏妲己 (1).pdf");
            FileInputStream fileInputStream = new FileInputStream(file);
            String FTP_HOST = "101.254.182.88";
            int PROT = 4695;
            String FTP_USERNAME = "aina";
            String FTP_PASSWORD = "C7LKZ8GfkWPY7nwArM";
            String FTP_PATH = "/"; // 根目录即可
            String filename = "111.pdf"; // 上传到FTP服务器上的文件名
            boolean b = uploadFile(FTP_HOST, PROT, FTP_USERNAME, FTP_PASSWORD, FTP_PATH,filename, fileInputStream);
            System.out.println("上传成功："+b);
        } catch (FileNotFoundException e) {
            log.error(e.toString(),e);
            System.out.println("上传失败：");
        }
    }
}
